<?php
namespace Hilaoyu\ComputeCloudClient\ctyun;

use Hilaoyu\Utils\UtilArr;
use Hilaoyu\Utils\UtilHttp;
use Hilaoyu\Utils\UtilStr;
use Hilaoyu\Utils\UtilSupport;

class CtyunClientVpc extends CtyunClient
{
    protected function baseUrl():string{
        return "https://ctvpc-global.ctapi.ctyun.cn";
    }

    public function listVpcs(string $vpcId = "", string $vpcname = "",string $regionId='')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        if($vpcId){
            $query['vpcID'] = $vpcId;
        }
        if($vpcname){
            $query['vpcName'] = $vpcname;
        }
        $result = $this->request("/v4/vpc/new-list",[],$query,"get");

        if(!$result){
            return null;
        }
        return (array)UtilArr::get($result, 'returnObj.vpcs');
    }

    public function createVpc(string $vpcname = "", string $network = "", string $ipv6Network = "",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "name" => $vpcname,
            "CIDR" => $network,
            "enableIpv6" => false,
        ];

        $result = $this->request("/v4/vpc/create",$params,[],"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.vpcID');
    }

    public function deleteVpc(string $vpcId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $params = [
            "regionID" => $regionId,
            "vpcID" => $vpcId,
        ];

        $result = $this->request("/v4/vpc/delete",$params,[],"post");

        return (bool)$result;
    }
    public function createSubnet(string $vpcId,string $network, string $vSwitchName = "", string $ipv6Network = "",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        if(!$vSwitchName){
            $vSwitchName = UtilStr::randString(8);
        }
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "vpcID" => $vpcId,
            "name" => $vSwitchName,
            "CIDR" => $network,
            "enableIpv6" => false,
        ];

        $result = $this->request("/v4/vpc/create-subnet",$params,[],"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.subnetID');
    }
    public function deleteSubnet(string $subnetId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $params = [
            "regionID" => $regionId,
            "subnetID" => $subnetId,
        ];

        $result = $this->request("/v4/vpc/delete-subnet",$params,[],"post");


        return (bool)$result;
    }
    public function listSubnets(string $vpcId="",string $subnetId="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
            "regionID" => $regionId,
            "vpcID" => $vpcId,
            "subnetID" => $subnetId,
            "pageSize" => 50,
        ];
        $params = [];

        $result = $this->request("/v4/vpc/new-list-subnet",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.subnets');
    }



    public function createPublicip(int $bandwidth = 50, string $cycleType = 'on_demand', string $demandBillingType = 'upflowc', string $lineType = 'bgp',  int $cycleCount = 1, string $name = null, $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "cycleType" => $cycleType,
            "name" => $name,
            "bandwidth" => $bandwidth,
            "demandBillingType" => $demandBillingType,
            //"lineType" => $lineType,
        ];
        if("on_demand" != $cycleType){
            $params['cycleCount']=intval($cycleCount);
            if($params['cycleCount'] < 1){
                $params['cycleCount'] = 1;
            }
        }

        $eipId = "";

        UtilSupport::reTry(function ()use (&$eipId,$params,$query){
            $result = $this->request("/v4/eip/create",$params,$query,"post");

            if(!$result){
                return true;
            }

            $eipId = UtilArr::get($result, 'returnObj.eipID');
            if($eipId){
                return true;
            }
        },5,12);
        return $eipId;
    }

    public function updatePublicip(string $eipId,string $name="", int $bandwidth = 50, $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
        ];
        if($bandwidth > 0){
            $params = [
                "clientToken" => $this->comCreateGuid(),
                "regionID" => $regionId,
                "eipID" => $eipId,
                "bandwidth" => $bandwidth,
            ];

            $result = $this->request("/v4/eip/modify-spec",$params,$query,"post");
            if(!$result){
                return false;
            }
        }
        if($name){
            $params = [
                "clientToken" => $this->comCreateGuid(),
                "regionID" => $regionId,
                "eipID" => $eipId,
                "name" => $name,
            ];

            $result = $this->request("/v4/eip/change-name",$params,$query,"post");
            if(!$result){
                return false;
            }
        }


        return true;
    }

    public function deletePublicip(string $eipId,  $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "eipID" => $eipId,
        ];

        $result = $this->request("/v4/eip/delete",$params,$query,"post");

        return (bool)$result;
    }
    public function deletePublicipByIp(string $ipaddress,  $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $ips = $this->listPublicips([],$ipaddress);

        if(is_null($ips)){
            return false;
        }
        $result = true;
        foreach ($ips as $ip ){
            $ipId = UtilArr::get($ip,"ID");
            if(!$ipId){
                continue;
            }

            $result = $result && $this->deletePublicip($ipId,$regionId);
        }

        return $result;
    }
    //todo debug
    public function associatePublicip(string $eipId,string $associationId,string $associationType="ecs", $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "eipID" => $eipId,
            "associationID" => $associationId,
            "associationType" => $this->formatEipAssociationType($associationType),
        ];

        $result = $this->request("/v4/eip/associate",$params,$query,"post");

        return (bool)$result;
    }
    //todo debug
    public function disassociatePublicip(string $eipId, $regionId = '')
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "eipID" => $eipId,
        ];

        $result = $this->request("/v4/eip/disassociate",$params,$query,"post");

        return (bool)$result;
    }

    public function listPublicips(array $ids=[],string $ip="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        if(!empty($ids)){
            $params["ids"] = $ids;
        }

        if($ip){
            $params["ip"] = (string)$ip;
        }

        $result = $this->request("/v4/eip/new-list",$params,$query,"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.eips');
    }
    public function showPublicip(string $id="",string $ip="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $ids = [];
        if($id){
            $ids = [$id];
        }
        $eips = $this->listPublicips($ids,$ip,$regionId);
        return UtilArr::get($eips,"0");

    }

    public function createRouteTable(string $vpcId,string $name="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "vpcID" => $vpcId,
        ];
        if($name){
            $params['name'] = $name;
        }

        $result = $this->request("/v4/vpc/route-table/create",$params,$query,"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.id');
    }
    public function deleteRouteTable(string $routeTableId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "routeTableID" => $routeTableId,
        ];

        $result = $this->request("/v4/vpc/route-table/delete",$params,$query,"post");

        return (bool)$result;
    }
    public function listRouteTables(string $routeTableId="",string $vpcId = "",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        $params = [
        ];
        if($vpcId){
            $query["vpcID"]=$vpcId;
        }
        if($routeTableId){
            $query["routeTableID"]=$routeTableId;
        }

        $result = $this->request("/v4/vpc/route-table/new-list",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.routeTables');
    }
    public function associateRouteTable(string $routeTableId,string $vSwitchId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "regionID" => $regionId,
            "routeTableID" => $routeTableId,
            "subnetID" => $vSwitchId,
        ];

        $result = $this->request("/v4/vpc/replace-subnet-route-table",$params,$query,"post");


        return (bool)$result;
    }
    public function disassociateRouteTable(string $vSwitchId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "regionID" => $regionId,
            "subnetID" => $vSwitchId,
        ];

        $result = $this->request("/v4/vpc/subnet-disassociate-route-table",$params,$query,"post");


        return (bool)$result;
    }
    public function createRouteTableRule(string $routeTableId,string $destinationNetwork, string $nextHopId, $nextHopType = 'vm',string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "regionID" => $regionId,
            "routeTableID" => $routeTableId,
            "destination" => $destinationNetwork,
            "ipVersion" => 4,
            "nextHopID" => $nextHopId,
            "nextHopType" => $nextHopType,
        ];

        $result = $this->request("/v4/vpc/route-table/create-rule",$params,$query,"post");


        return (bool)$result;
    }
    public function deleteRouteTableRule(string $routeRuleId,string $routeTableId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "routeRuleIDs" => [$routeRuleId],
            "routeTableID" => $routeTableId,
        ];

        $result = $this->request("/v4/vpc/route-table/delete-rules",$params,$query,"post");


        return (bool)$result;
    }
    public function listRouteTableRules(string $routeTableID,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
            "regionID" => $regionId,
            "routeTableID" => $routeTableID,
            "pageSize" => 50,
        ];
        $params = [

        ];

        $result = $this->request("/v4/vpc/route-table/new-list-rules",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.routeRules');
    }



    public function createSecurityGroup(string $name, string $vpcId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,

            "vpcID" => $vpcId,
            "name" => $name,
        ];

        $result = $this->request("/v4/vpc/create-security-group",$params,$query,"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.securityGroupID');
    }
    public function deleteSecurityGroup(string $id, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,

            "securityGroupID" => $id,
        ];

        $result = $this->request("/v4/vpc/delete-security-group",$params,$query,"post");


        return (bool)$result;
    }
    public function listSecurityGroups(array $ids = [], string $name = "", string $vpcId = "", int $pageSize = 50, int $pageNumber = 1,  string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        if($pageSize > 50){
            $pageSize = 50;
        }
        if($pageNumber < 1){
            $pageNumber = 1;
        }
        $query = [
            "regionID" => $regionId,

            "pageNumber" => $pageNumber,
            "pageSize" => $pageSize,
        ];
        $params = [

        ];

        if($vpcId){
            $query["vpcID"] = $vpcId;
        }
        $queryContent = $ids;
        if($name){
            $queryContent[] = $name;
        }
        if(!empty($queryContent)){
            $query["queryContent"] = implode(",",$queryContent);
        }

        $result = $this->request("/v4/vpc/new-query-security-groups",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.securityGroups');
    }
    public function createSecurityGroupRuleIngress(string $securityGroupId,string $portRange, string $action = "accept",int $priority=1,string $protocol="ANY",string $destCidrIp="0.0.0.0/0", string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $protocol = UtilStr::upper($protocol);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "securityGroupID" => $securityGroupId,
        ];

        $securityGroupRule = [
            "direction"=>"ingress",
            "remoteType"=>0,
            "action"=>$action,
            "priority"=>$priority,
            "protocol"=>$protocol,
            "ethertype"=>"IPv4",
            "destCidrIp"=>$destCidrIp,
            "range"=>$portRange,
        ];
        if("ANY" != $protocol){
            $securityGroupRule["range"] = $portRange;
        }
        $params['securityGroupRules'][] = $securityGroupRule;

        $result = $this->request("/v4/vpc/create-security-group-ingress",$params,$query,"post");


        return (bool)$result;
    }
    public function deleteSecurityGroupRuleIngress(string $securityGroupId,string $securityGroupRuleId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "securityGroupID" => $securityGroupId,
            "securityGroupRuleID" => $securityGroupRuleId,
        ];
        $result = $this->request("/v4/vpc/revoke-security-group-ingress",$params,$query,"post");

        return (bool)$result;
    }
    public function createSecurityGroupRuleEgress(string $securityGroupId,string $portRange, string $action = "accept",int $priority=1,string $protocol="ANY",string $destCidrIp="0.0.0.0/0", string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $protocol = UtilStr::upper($protocol);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "securityGroupID" => $securityGroupId,
        ];

        $securityGroupRule = [
            "direction"=>"egress",
            "remoteType"=>0,
            "action"=>$action,
            "priority"=>$priority,
            "protocol"=>$protocol,
            "ethertype"=>"IPv4",
            "destCidrIp"=>$destCidrIp,
            "range"=>$portRange,
        ];
        if("ANY" != $protocol){
            $securityGroupRule["range"] = $portRange;
        }
        $params['securityGroupRules'][] = $securityGroupRule;

        $result = $this->request("/v4/vpc/create-security-group-egress",$params,$query,"post");

        return (bool)$result;
    }
    public function deleteSecurityGroupRuleEgress(string $securityGroupId,string $securityGroupRuleId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "securityGroupID" => $securityGroupId,
            "securityGroupRuleID" => $securityGroupRuleId,
        ];
        $result = $this->request("/v4/vpc/revoke-security-group-egress",$params,$query,"post");

        return (bool)$result;
    }
    public function listSecurityGroupRules(string $securityGroupId,string $direction="", string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        if(!$direction){
            $direction = "all";
        }
        $query = [
            "regionID" => $regionId,
            "securityGroupID" => $securityGroupId,
            "direction" => $direction,
        ];
        $params = [

        ];

        $result = $this->request("/v4/vpc/describe-security-group-attribute",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj.securityGroupRuleList');
    }


    public function createPort(string $subnetId,string $name="",array $securityGroupIds=[], string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "subnetID" => $subnetId,
        ];
        if($name){
            $params["name"] = $name;
        }
        if(!empty($securityGroupIds)){
            $params["securityGroupIds"] = $securityGroupIds;
        }

        $result = $this->request("/v4/ports/create",$params,$query,"post");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj');
    }
    public function showPort(string $portId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [
            "regionID" => $regionId,
            "networkInterfaceID" => $portId,
        ];
        $params = [

        ];

        $result = $this->request("/v4/ports/show",$params,$query,"get");

        if(!$result){
            return;
        }
        return UtilArr::get($result, 'returnObj');
    }
    public function showPorts(string $subnetId="",string $deviceId="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $pageNo = 1;
        $query = [
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        $params = [
        ];
        if($subnetId){
            $query["subnetID"] = $subnetId;
        }
        if($deviceId){
            $query["deviceID"] = $deviceId;
        }


        $ports = [];
        do{
            $query["pageNo"] = $pageNo;
            $result = $this->request("/v4/ports/new-list",$params,$query,"get");
            if(!$result){
                return;
            }
            $ports = array_merge($ports,(array)UtilArr::get($result, 'returnObj.ports'));
            $totalPage = UtilArr::get($result, 'returnObj.totalPage');
            if($pageNo >= $totalPage){
                break;
            }
            $pageNo++;
        }while(true);


        return $ports;
    }
    public function portAttachInstance(string $portId,string $instanceId,string $azName,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "azName" => $azName,
            "networkInterfaceID" => $portId,
            "instanceID" => $instanceId,
            "instanceType" => 3,
        ];

        $result = $this->request("/v4/ports/attach",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }
    public function portDetachInstance(string $portId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "networkInterfaceID" => $portId,
        ];

        $result = $this->request("/v4/ports/detach",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }
    public function deletePort(string $portId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params = [
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "networkInterfaceID" => $portId,
        ];

        $result = $this->request("/v4/ports/delete",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }




    protected function formatEipAssociationType( $t )
    {
        $arr = [
            "ecs" => 1,
            "vip" => 2,
            "bm" => 3,
        ];
        return UtilArr::get($arr,UtilStr::lower($t),1);
    }





}
